
'''
Control vmware from Python.  Used the vmware server api.

@author: Michael Eddington
@version: $Id$
'''

#
# Copyright (c) 2007 Michael Eddington
#
# Permission is hereby granted, free of charge, to any person obtaining a copy 
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights 
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
# copies of the Software, and to permit persons to whom the Software is 
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in	
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#

# Authors:
#   Michael Eddington (mike@phed.org)

# $Id$

import sys
from ctypes import *

if sys.platform[:3] == 'win':
	vdll = cdll.LoadLibrary(sys.modules[__name__].__file__[:-6] + "vix.dll")
else:
	vdll = cdll.LoadLibrary(sys.modules[__name__].__file__[:-6] + "libvixAllProducts.so")

class Vix:
	
	VIX_API_VERSION      = -1
	VIX_INVALID_HANDLE   = 0
	VIX_HANDLETYPE_NONE          = 0
	VIX_HANDLETYPE_HOST          = 2
	VIX_HANDLETYPE_VM            = 3
	VIX_HANDLETYPE_NETWORK       = 5
	VIX_HANDLETYPE_JOB           = 6
	VIX_HANDLETYPE_SNAPSHOT      = 7

	VIX_OK                                       = 0
	
	# General errors
	VIX_E_FAIL                                   = 1
	VIX_E_OUT_OF_MEMORY                          = 2
	VIX_E_INVALID_ARG                            = 3
	VIX_E_FILE_NOT_FOUND                         = 4
	VIX_E_OBJECT_IS_BUSY                         = 5
	VIX_E_NOT_SUPPORTED                          = 6
	VIX_E_FILE_ERROR                             = 7
	VIX_E_DISK_FULL                              = 8
	VIX_E_INCORRECT_FILE_TYPE                    = 9
	VIX_E_CANCELLED                              = 10
	VIX_E_FILE_READ_ONLY                         = 11
	VIX_E_FILE_ALREADY_EXISTS                    = 12
	VIX_E_FILE_ACCESS_ERROR                      = 13
	VIX_E_REQUIRES_LARGE_FILES                   = 14
	VIX_E_FILE_ALREADY_LOCKED                    = 15
	
	# Handle Errors
	VIX_E_INVALID_HANDLE                         = 1000
	VIX_E_NOT_SUPPORTED_ON_HANDLE_TYPE           = 1001
	VIX_E_TOO_MANY_HANDLES                       = 1002
	
	# XML errors 
	VIX_E_NOT_FOUND                              = 2000
	VIX_E_TYPE_MISMATCH                          = 2001
	VIX_E_INVALID_XML                            = 2002
	
	# VM Control Errors 
	VIX_E_TIMEOUT_WAITING_FOR_TOOLS              = 3000
	VIX_E_UNRECOGNIZED_COMMAND                   = 3001
	VIX_E_OP_NOT_SUPPORTED_ON_GUEST              = 3003
	VIX_E_PROGRAM_NOT_STARTED                    = 3004
	VIX_E_VM_NOT_RUNNING                         = 3006
	VIX_E_VM_IS_RUNNING                          = 3007
	VIX_E_CANNOT_CONNECT_TO_VM                   = 3008
	VIX_E_POWEROP_SCRIPTS_NOT_AVAILABLE          = 3009
	VIX_E_NO_GUEST_OS_INSTALLED                  = 3010
	VIX_E_VM_INSUFFICIENT_HOST_MEMORY            = 3011
	VIX_E_SUSPEND_ERROR                          = 3012
	VIX_E_VM_NOT_ENOUGH_CPUS                     = 3013
	VIX_E_HOST_USER_PERMISSIONS                  = 3014
	VIX_E_GUEST_USER_PERMISSIONS                 = 3015
	VIX_E_TOOLS_NOT_RUNNING                      = 3016
	VIX_E_GUEST_OPERATIONS_PROHIBITED            = 3017
	VIX_E_ANON_GUEST_OPERATIONS_PROHIBITED       = 3019
	VIX_E_ROOT_GUEST_OPERATIONS_PROHIBITED       = 3021
	VIX_E_MISSING_ANON_GUEST_ACCOUNT             = 3023
	VIX_E_CANNOT_AUTHENTICATE_WITH_GUEST         = 3024
	VIX_E_UNRECOGNIZED_COMMAND_IN_GUEST          = 3025
	
	
	# VM Errors
	VIX_E_VM_NOT_FOUND                           = 4000
	VIX_E_NOT_SUPPORTED_FOR_VM_VERSION           = 4001
	VIX_E_CANNOT_READ_VM_CONFIG                  = 4002
	VIX_E_TEMPLATE_VM                            = 4003
	VIX_E_VM_ALREADY_LOADED                      = 4004
	
	# Property Errors 
	VIX_E_UNRECOGNIZED_PROPERTY                  = 6000
	VIX_E_INVALID_PROPERTY_VALUE                 = 6001
	VIX_E_READ_ONLY_PROPERTY                     = 6002
	VIX_E_MISSING_REQUIRED_PROPERTY              = 6003
	
	# Completion Errors
	VIX_E_BAD_VM_INDEX                           = 8000

	VIX_PROPERTYTYPE_ANY             = 0
	VIX_PROPERTYTYPE_INTEGER         = 1
	VIX_PROPERTYTYPE_STRING          = 2
	VIX_PROPERTYTYPE_BOOL            = 3
	VIX_PROPERTYTYPE_HANDLE          = 4
	VIX_PROPERTYTYPE_INT64           = 5
	
	VIX_PROPERTY_NONE                        = 0
	
	# VIX_HANDLETYPE_VM properties */
	VIX_PROPERTY_VM_NUM_VCPUS                = 101
	VIX_PROPERTY_VM_VMX_PATHNAME             = 103 
	VIX_PROPERTY_VM_VMTEAM_PATHNAME          = 105 
	VIX_PROPERTY_VM_MEMORY_SIZE              = 106 
	VIX_PROPERTY_VM_IN_VMTEAM                = 128
	VIX_PROPERTY_VM_POWER_STATE              = 129
	VIX_PROPERTY_VM_TOOLS_STATE              = 152
	VIX_PROPERTY_VM_IS_RUNNING               = 196
	
	# Result properties; these are returned by various procedures */
	VIX_PROPERTY_JOB_RESULT_ERROR_CODE                 = 3000
	VIX_PROPERTY_JOB_RESULT_VM_IN_GROUP                = 3001
	VIX_PROPERTY_JOB_RESULT_USER_MESSAGE               = 3002
	VIX_PROPERTY_JOB_RESULT_EXIT_CODE                  = 3004
	VIX_PROPERTY_JOB_RESULT_COMMAND_OUTPUT             = 3005
	VIX_PROPERTY_JOB_RESULT_HANDLE                     = 3010
	VIX_PROPERTY_JOB_RESULT_GUEST_OBJECT_EXISTS        = 3011
	VIX_PROPERTY_JOB_RESULT_GUEST_PROGRAM_ELAPSED_TIME = 3017
	VIX_PROPERTY_JOB_RESULT_GUEST_PROGRAM_EXIT_CODE    = 3018
	VIX_PROPERTY_JOB_RESULT_ITEM_NAME                  = 3035
	VIX_PROPERTY_JOB_RESULT_FOUND_ITEM_DESCRIPTION     = 3036
	VIX_PROPERTY_JOB_RESULT_SHARED_FOLDER_COUNT        = 3046
	VIX_PROPERTY_JOB_RESULT_SHARED_FOLDER_HOST         = 3048
	VIX_PROPERTY_JOB_RESULT_SHARED_FOLDER_FLAGS        = 3049
	VIX_PROPERTY_JOB_RESULT_PROCESS_ID                 = 3051
	VIX_PROPERTY_JOB_RESULT_PROCESS_OWNER              = 3052
	VIX_PROPERTY_JOB_RESULT_PROCESS_COMMAND            = 3053
	VIX_PROPERTY_JOB_RESULT_FILE_FLAGS                 = 3054
	VIX_PROPERTY_JOB_RESULT_PROCESS_START_TIME         = 3055
	VIX_PROPERTY_JOB_RESULT_VM_VARIABLE_STRING         = 3056
	VIX_PROPERTY_JOB_RESULT_PROCESS_BEING_DEBUGGED     = 3057
	VIX_PROPERTY_JOB_RESULT_SCREEN_IMAGE_SIZE          = 3058
	VIX_PROPERTY_JOB_RESULT_SCREEN_IMAGE_DATA          = 3059
	VIX_PROPERTY_JOB_RESULT_FILE_SIZE                  = 3061
	VIX_PROPERTY_JOB_RESULT_FILE_MOD_TIME              = 3062
	
	# Event properties; these are sent in the moreEventInfo for some events. */
	VIX_PROPERTY_FOUND_ITEM_LOCATION         = 4010

	VIX_EVENTTYPE_JOB_COMPLETED          = 2
	VIX_EVENTTYPE_JOB_PROGRESS           = 3
	VIX_EVENTTYPE_FIND_ITEM              = 8
	VIX_EVENTTYPE_CALLBACK_SIGNALLED     = 2  # Deprecated - Use VIX_EVENTTYPE_JOB_COMPLETED instead.

	VIX_HOSTOPTION_USE_EVENT_PUMP        = 0x0008
	VIX_SERVICEPROVIDER_VMWARE_SERVER    = 2
	VIX_SERVICEPROVIDER_DEFAULT          = 1
	
	VIX_VMPOWEROP_NORMAL = 0
	
	#####################################################
	
	def __init__(self):
		self.isConnected = False
		
		self.vix = vdll
		
		self.VixHost_Connect = self.vix.VixHost_Connect
		self.VixHost_Connect.argtypes = [c_int, c_int, c_char_p, c_int, c_char_p, c_char_p, c_int, c_int, c_void_p, c_void_p ]
		self.VixJob_Wait = self.vix.VixJob_Wait
		self.VixJob_Wait.argtypes = [c_int, c_int, c_void_p, c_int]
		self.VixJob_Wait2 = self.vix.VixJob_Wait
		self.VixJob_Wait2.argtypes = [c_int, c_int]
		#self.VixVM_GetNamedSnapshot = self.vix.VixVM_GetNamedSnapshot
		#self.VixVM_GetNamedSnapshot = [c_int, c_char_p, c_void_p]
		self.VixVM_GetRootSnapshot = self.vix.VixVM_GetRootSnapshot
		self.VixVM_GetRootSnapshot.argtypes = [c_int, c_int, c_void_p]
		self.VixVM_Unpause = self.vix.VixVM_Unpause
		self.VixVM_Unpause.argtypes = [c_int, c_int, c_int, c_void_p, c_void_p]
		self.VixVM_Pause = self.vix.VixVM_Pause
		self.VixVM_Pause.argtypes = [c_int, c_int, c_int, c_void_p, c_void_p]
		self.VixVM_Suspend = self.vix.VixVM_Suspend
		self.VixVM_Suspend.argtypes = [c_int, c_int, c_void_p, c_void_p]
		self.VixVM_WaitForToolsInGuest = self.vix.VixVM_WaitForToolsInGuest
		self.VixVM_WaitForToolsInGuest.argtypes = [c_int, c_int, c_void_p, c_void_p]
		self.VixVM_LoginInGuest = self.vix.VixVM_LoginInGuest
		self.VixVM_LoginInGuest.argtypes = [c_int, c_char_p, c_char_p, c_int, c_void_p, c_void_p]
		self.VixVM_FileExistsInGuest = self.vix.VixVM_FileExistsInGuest
		self.VixVM_FileExistsInGuest.argtypes = [c_int, c_char_p, c_void_p, c_void_p]

	def Connect(self, hostname = None, hostport = 0, username = None, password = None):
		self.jobHandle = Vix.VIX_INVALID_HANDLE
		self.vmHandle = Vix.VIX_INVALID_HANDLE
		self.hostHandle = Vix.VIX_INVALID_HANDLE
		
		self.jobHandle = self.VixHost_Connect(Vix.VIX_API_VERSION,
			Vix.VIX_SERVICEPROVIDER_DEFAULT,
			hostname,
			hostport,
			username,
			password,
			0,
			Vix.VIX_INVALID_HANDLE,
			None,
			None);
		
		hostHandle = c_int()		
		err = self.VixJob_Wait(self.jobHandle, Vix.VIX_PROPERTY_JOB_RESULT_HANDLE,
			byref(hostHandle), Vix.VIX_PROPERTY_NONE)
		
		self.hostHandle = hostHandle.value
		
		self.vix.Vix_ReleaseHandle(self.jobHandle)
		
		if err != Vix.VIX_OK:
			raise VixException(err)
	
	def Open(self, vmxFile):
		self.jobHandle = self.vix.VixVM_Open(self.hostHandle, vmxFile, None, None)
		
		vmHandle = c_int()
		err = self.VixJob_Wait(self.jobHandle, Vix.VIX_PROPERTY_JOB_RESULT_HANDLE,
			byref(vmHandle), Vix.VIX_PROPERTY_NONE)
		self.vix.Vix_ReleaseHandle(self.jobHandle);
		
		self.vmHandle = vmHandle.value
		
		if err != Vix.VIX_OK:
			raise VixException(err)

	def PowerOn(self):
		self.jobHandle = self.vix.VixVM_PowerOn(self.vmHandle, Vix.VIX_VMPOWEROP_NORMAL,
			Vix.VIX_INVALID_HANDLE, None, None);
		err = self.VixJob_Wait2(self.jobHandle, Vix.VIX_PROPERTY_NONE)
		self.vix.Vix_ReleaseHandle(self.jobHandle);
		
		if err != Vix.VIX_OK:
			raise VixException(err)
		
	def PowerOff(self):
		self.jobHandle = self.vix.VixVM_PowerOff(self.vmHandle, Vix.VIX_VMPOWEROP_NORMAL,
			None, None);
		err = self.VixJob_Wait2(self.jobHandle, Vix.VIX_PROPERTY_NONE)
		self.vix.Vix_ReleaseHandle(self.jobHandle);
		
		if err != Vix.VIX_OK:
			raise VixException(err)
	
	def CreateSnaphot(self, name, description = None):
		self.jobHandle = self.vix.VixVM_CreateSnapshot(self.vmHandle, name,
			description, 0, Vix.VIX_INVALID_HANDLE, None, None)
		
		vmHandle = c_int()
		err = self.VixJob_Wait(self.snapshotHandle, Vix.VIX_PROPERTY_JOB_RESULT_HANDLE,
			byref(vmHandle), Vix.VIX_PROPERTY_NONE)
		self.vix.Vix_ReleaseHandle(self.jobHandle);
		self.vmHandle = vmHandle.value
		
		if err != Vix.VIX_OK:
			raise VixException(err)
	
	#def RevertToNamedSnapshot(self, name):
	#	self.GetNamedSnapshot(name)
	#	self.RevertToSnapshot()
	
	def RevertToSnapshot(self):
		# get snapshot handle
		
		self.jobHandle = self.vix.VixVM_RevertToSnapshot(self.vmHandle,
			self.snapshotHandle, 0, Vix.VIX_INVALID_HANDLE, None, None)
		err = self.VixJob_Wait2(self.jobHandle, Vix.VIX_PROPERTY_NONE)
		self.vix.Vix_ReleaseHandle(self.jobHandle)
		
		if err != Vix.VIX_OK:
			raise VixException(err)
	
	def GetRootSnapshot(self, index = 0):
		snapshotHandle = c_int()
		err = self.VixVM_GetRootSnapshot(self.vmHandle, index, byref(snapshotHandle))
		self.snapshotHandle = snapshotHandle.value
		if err != Vix.VIX_OK:
			raise VixException(err)
		
	#def GetNamedSnapshot(self, name):
	#	snapshotHandle = c_int()
	#	err = self.VixVM_GetNamedSnapshot(self.vmHandle, name, byref(snapshotHandle))
	#	self.snapshotHandle = snapshotHandle.value
	#	if err != VIX_OK:
	#		raise Exception("VixVM_GetNamedSnapshot Failed")
	
	def Disconnect(self):
		self.vix.VixHost_Disconnect(self.hostHandle)
	
	def WaitForToolsInGuest(self, timeout=0):
		self.jobHandle = self.VixVM_WaitForToolsInGuest(self.vmHandle, timeout,
			None, None)
		err = self.VixJob_Wait2(self.jobHandle, Vix.VIX_PROPERTY_NONE)
		self.vix.Vix_ReleaseHandle(self.jobHandle)
		
		if err != Vix.VIX_OK:
			# Not fatal in this case
			return False
		else:
			return True
	
	def LoginInGuest(self, username, password):
		self.jobHandle = self.VixVM_LoginInGuest(self.vmHandle, username, password,
			0, None, None)
		err = self.VixJob_Wait2(self.jobHandle, Vix.VIX_PROPERTY_NONE)
		self.vix.Vix_ReleaseHandle(self.jobHandle)
		
		if err != Vix.VIX_OK:
			raise VixException(err)
	
	def FileExistsInGuest(self, filename):
		objExists = c_int()
		self.jobHandle = self.VixVM_FileExistsInGuest(self.vmHandle, filename,
			None, None)
		err = self.VixJob_Wait(self.jobHandle, Vix.VIX_PROPERTY_JOB_RESULT_GUEST_OBJECT_EXISTS,
			byref(objExists), Vix.VIX_PROPERTY_NONE)
		self.vix.Vix_ReleaseHandle(self.jobHandle)

		if err != Vix.VIX_OK:
			raise VixException(err)

		return bool(objExists.value)
		
	def Suspend(self):
		self.jobHandle = self.VixVM_Suspend(self.vmHandle, 0, None, None)
		err = self.VixJob_Wait2(self.jobHandle, Vix.VIX_PROPERTY_NONE)
		self.vix.Vix_ReleaseHandle(self.jobHandle)

		if err != Vix.VIX_OK:
			raise VixException(err)
		
	def Pause(self):
		self.jobHandle = self.VixVM_Pause(self.vmHandle, 0, Vix.VIX_INVALID_HANDLE,
			None, None)
		err = self.VixJob_Wait2(self.jobHandle, Vix.VIX_PROPERTY_NONE)
		self.vix.Vix_ReleaseHandle(self.jobHandle)

		if err != Vix.VIX_OK:
			raise VixException(err)

	def Unpause(self):
		self.jobHandle = self.VixVM_Unpause(self.vmHandle, 0, Vix.VIX_INVALID_HANDLE,
			None, None)
		err = self.VixJob_Wait2(self.jobHandle, Vix.VIX_PROPERTY_NONE)
		self.vix.Vix_ReleaseHandle(self.jobHandle)

		if err != Vix.VIX_OK:
			raise VixException(err)


class VixException(Exception):
	def __init__(self,errorCode):
		self.vix = vdll
		self.vix.Vix_GetErrorText.restype = c_char_p
		self.errorCode = errorCode
		self.message = self.vix.Vix_GetErrorText(self.errorCode, None)
	
	def __str__(self):
		return repr(self.message)

# end
